package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstants;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.AlertNotifyDto;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.*;
import com.zzyl.service.AlarmRuleService;
import com.zzyl.vo.AlertRuleVo;
import com.zzyl.vo.DeviceDataVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AlarmRuleServiceImpl implements AlarmRuleService {

    @Autowired
    private AlarmRuleMapper alarmRuleMapper;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private NursingElderMapper nursingElderMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private AlertDataMapper alertDataMapper;

    /**
     * 创建报警规则
     *
     * @param alertRuleDto
     */
    @Override
    public void create(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = new AlertRule();
        BeanUtils.copyProperties(alertRuleDto, alertRule);
        alarmRuleMapper.create(alertRule);
    }

    /**
     * 分页查询报警规则
     *
     * @param pageNum
     * @param pageSize
     * @param alertRuleName
     * @param functionName
     * @param productKey
     * @return
     */
    @Override
    public PageResponse<AlertRuleVo> page(Integer pageNum, Integer pageSize, String alertRuleName, String functionName, String productKey) {
        // 分页
        PageHelper.startPage(pageNum, pageSize);
        // 查询数据
        Page<AlertRuleVo> alertRules =alarmRuleMapper.getPage(alertRuleName, functionName, productKey);
        // 封装分页数据
        PageResponse<AlertRuleVo> pageResponse = new PageResponse<>();
        pageResponse.setTotal(alertRules.getTotal());
        pageResponse.setPage(pageNum);
        pageResponse.setPageSize(pageSize);
        pageResponse.setRecords(alertRules.getResult());

        pageResponse.getRecords().forEach(item -> {
            String rules = item.getFunctionName()+item.getOperator()+item.getValue();
            item.setRules(rules);
        });
        // 返回
        return pageResponse;
    }

    /**
     * 根据ID查询报警规则
     *
     * @param id
     * @return
     */
    @Override
    public AlertRuleVo read(Integer id) {
        AlertRuleVo Vo = alarmRuleMapper.read(id);
        return Vo;
    }

    /**
     * 更新报警规则
     *
     * @param id
     * @param alertRuleDto
     */
    @Override
    public void update(Long id, AlertRuleDto alertRuleDto) {
        AlertRule alertRule = new AlertRule();
        BeanUtils.copyProperties(alertRuleDto, alertRule);
        alertRule.setUpdateTime(LocalDateTime.now());
        alertRule.setId(id);
        alarmRuleMapper.update(alertRule);
    }

    /**
     * 删除报警规则
     *
     * @param id
     */
    @Override
    @Transactional
    public void delete(Integer id) {
        alarmRuleMapper.delete(id);
    }

    /**
     * 启用-禁用
     *
     * @param id
     * @param status
     */
    @Override
    public void updateStatus(Long id, Integer status) {
        alarmRuleMapper.updateStatus(id, status);
    }

    @Override
    public void alertFilter(DeviceDataVo vo) {
        //1.判断上报时间，是否已经过去太久了，如果太久了，那么就属于积压的消息，积压的消息不处理
        //注意事项：第一个参数写的是小的时间，第二个参数写的是大的时间，，第三个参数写的是时间单位
        if(LocalDateTimeUtil.between(vo.getAlarmTime(),LocalDateTime.now(), ChronoUnit.SECONDS) >60 ){
            return;
        }
        //2.通过产品和functionid找到产品的校验规则（全局设备的）
        List<AlertRule> ruleList = alarmRuleMapper.queryAlarm(vo.getProductKey(), null, vo.getFunctionId());
        ruleList = CollUtil.isEmpty(ruleList) ? new ArrayList<>() : ruleList;
        //3.通过设备的iotid和functionid找到设备的校验规则
        List<AlertRule> deviceRuleList = alarmRuleMapper.queryAlarm(null, vo.getIotId(), vo.getFunctionId());
        //4.合并所有的规则，然后去重
        ruleList.addAll(deviceRuleList);
        ruleList = ruleList.stream().distinct().collect(Collectors.toList());
        //5.判断规则是否为空，如果为空则遍历下一个规则，继续校验数据
        if (CollUtil.isEmpty(ruleList)){
            return;
        }
        //6.遍历每一个规则，然后用当前的规则校验数据
        ruleList.forEach(rule -> {
            deviceDateAlarmHandle(rule, vo);
        });
    }

    private void deviceDateAlarmHandle(AlertRule rule, DeviceDataVo vo) {
        //1.判断数据上报的时间，是否在规则校验的时间之内，如果不在则返回
        String effectivePeriod = rule.getAlertEffectivePeriod();
        String[] strs = effectivePeriod.split("~");
        LocalTime startTime = LocalTime.parse(strs[0]);
        LocalTime endTime = LocalTime.parse(strs[1]);
        LocalTime alarmTime = LocalDateTimeUtil.of(vo.getAlarmTime()).toLocalTime();
        if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)){
            return;
        }
        //2.判断上报的数据是否达到了规则阈值，如果没有达到，则删除redis中储存的连续次数
        //则删除redis中存储的连续次数key：前缀+产品的key+设备id+功能id
        String countKey = CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + ":" + vo.getProductKey() + ":" + vo.getIotId() + ":" + vo.getFunctionId();
        //x==y返回0，x<y返回小于0的数，x>y返回大于0的数
        int result = NumberUtil.compare(rule.getValue(),Float.parseFloat(vo.getDataValue()));
        if ((">=".equals(rule.getOperator()) && result > 0) || ("<".equals(rule.getOperator()) && result <= 0)){
            redisTemplate.delete(countKey);
            return;
        }
        //3.如果达到了规则阈值，则判断是否有沉默周期，如果有则返回，如果没有继续校验
        String silentKey = CacheConstants.ALERT_SILENT_PREFIX + ":" + vo.getProductKey() + ":" + vo.getIotId() + ":" + vo.getFunctionId();
        String slientValue = redisTemplate.opsForValue().get(silentKey);
        if (ObjectUtil.isNotEmpty(slientValue)){
            return;
        }
        //4.从redis中获取当前这个产品的这个设备的这个维度的连续警告次数
        String count = redisTemplate.opsForValue().get(countKey);
        //5.判断是否为空，如果为空则设置为0，然后+1，如果不为0，则在原来的基础上+1
        int _count = ObjectUtil.isEmpty(count) ? 0 : Integer.parseInt(count);
        _count++;
        //6.判断是否达到持续周期设置的值，如果没有达到阈值，则重新写入数据到redis
        if (_count < rule.getDuration()){
            redisTemplate.opsForValue().set(countKey,_count+"");
            return;
        }
        //7.如果达到了阈值，删除统计的次数，并设置静默值
        redisTemplate.delete(countKey);
        //设置静默值
        redisTemplate.opsForValue().set(silentKey,"1",rule.getAlertSilentPeriod(), TimeUnit.MINUTES);
        //8.查询维修人员或者护工
        List<Long> ids = new ArrayList<>();
        if (vo.getLocationType().equals(SuperConstant.DATA_STATE_1)){
            //查询维修人员
            ids = sysUserRoleMapper.selectUserByRoleName("维修工");

        }else {
            if (vo.getPhysicalLocationType().equals("-1")){
                //绑定在老人身上的随身设备
                ids = deviceMapper.selectNursingIdByIotId(vo.getIotId());
            }else {
                //绑定的是床位
                ids = deviceMapper.selectNursingIdByBedAndIotId(vo.getIotId());
            }


        }
        //9.查询管理员并合并2个数据
        List<Long> superIds = sysUserRoleMapper.selectUserByRoleName("超级管理员");
        ids.addAll(superIds);
        //10.批量插入告警数据
        List<AlertData> list = insertAlertData(vo,rule,ids);
        //11.通知对应的工作人员
        websocketNotify(rule, list.get(0), ids);
    }

    private void websocketNotify(AlertRule alertRule, AlertData alertData, List<Long> ids) {
        AlertNotifyDto dto = new AlertNotifyDto();
        BeanUtils.copyProperties(alertData,dto);
        dto.setFunctionName(alertRule.getFunctionName());
        dto.setAlertDataType(alertData.getType());
        dto.setNotifyType(1);
    }

    private List<AlertData> insertAlertData(DeviceDataVo deviceDataVo, AlertRule alertRule, List<Long> consumerIds) {
        String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
        AlertData alertData = BeanUtil.toBean(deviceDataVo, AlertData.class);
        alertData.setAlertRuleId(alertRule.getId());
        alertData.setAlertReason(alertReason);
        alertData.setType(alertRule.getAlertDataType());
        alertData.setStatus(0);

        List<AlertData> list = consumerIds.stream().map(id -> {
            AlertData dbAlertData = BeanUtil.toBean(alertData, AlertData.class);
            dbAlertData.setUserId(id);
            return dbAlertData;
        }).collect(Collectors.toList());
        alertDataMapper.batchInsert(list);
        return list;
    }
}
